#ifndef DW_GRAPHICS_RENDERER_H
#	define DW_GRAPHICS_RENDERER_H

#include "color.h"
#include "vertexformat.h"
#include "pixelformat.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "query.h"

#include "maths/cuboid.h"

#include <stack>

#include "graphics/rendertarget.h"

#include "graphics/backend.h"

namespace dw {
	namespace graphics {

class Font;
class Pass;
class Light;
class Material;
class IPixelBuffer;
class ITexture;
class Program;

using namespace system;
using namespace maths;

namespace primitive {

enum types
{
	points = 0,
	linestrip,
	lineloop,
	lines,
	trianglestrip,
	trianglefan,
	triangles,
	linesadjacency,
	linestripadjacency,
	trianglesadjacency,
	trianglestripadjacency,
	patches,
	types_count
};

inline GLenum glType(types t)
{
	static const GLenum glTypes[types_count] = {
		GL_POINTS,
		GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES,
		GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES,
		GL_LINES_ADJACENCY, GL_LINE_STRIP_ADJACENCY,
		GL_TRIANGLES_ADJACENCY, GL_TRIANGLE_STRIP_ADJACENCY,
		GL_PATCHES
	};

	return glTypes[t];
}

} // namespace primitive

class Renderer
{
public:

	struct DataType
	{
		enum
		{
			BYTE,
			UNSIGNED_BYTE,
			SHORT,
			UNSIGNED_SHORT,
			INT,
			UNSIGNED_INT,
			FLOAT,
			DOUBLE
		};
	};

	struct PrimitiveType
	{
		enum
		{
			POINTS,
			LINES,
			TRIANGLES,
			TRIANGLE_STRIP,
			QUADS
		};
	};
	
	struct CullMode
	{
		enum
		{ 
			FRONT, 
			BACK, 
			FRONT_AND_BACK 
		};
	};
	
	struct FillMode 
	{
		enum
		{ 
			POINT,
			WIREFRAME,
			SOLID,
			COUNT
		};
	};
	
	struct ShadeMode	
	{
		enum
		{ 
			FLAT, 
			GOURAUD 
		};
	};
	
	enum MatrixMode	
	{ 
		MODELVIEW, 
		PROJECTION, 
		TEXTURE 
	};
	
	enum CmpFunc		
	{ 
		GREATER, 
		LESS, 
		GEQUAL, 
		LEQUAL, 
		NOTEQUAL, 
		EQUAL, 
		ALWAYS, 
		CMP_NEVER 
	};

	struct BlendMode
	{
		enum
		{ 
			ZERO,
			ONE,
			DST_COLOR,
			SRC_COLOR,
			INV_DST_COLOR,
			INV_SRC_COLOR,
			SRC_ALPHA,
			INV_SRC_ALPHA,
			DST_ALPHA, 
			INV_DST_ALPHA,
			SRC_ALPHA_SATURATE,
			CONSTANT_COLOR,
			INV_CONSTANT_COLOR,
			CONSTANT_ALPHA,
			INV_CONSTANT_ALPHA
		};
	};

	struct BlendOp
	{
		enum		
		{	
			ADD, 
			SUBSTRACT, 
			INV_SUBSTRACT, 
			MIN, 
			MAX 
		};
	};
	
	struct StencilOp
	{
		enum	
		{ 
			INCR_WRAP, 
			DECR_WRAP, 
			KEEP,
			INCR,
			DECR 
		};
	};

	enum RenderState
	{
		RS_CULLING,
		RS_CULL_MODE,
		RS_LIGHTING,
		RS_TEXTURE_1D,
		RS_TEXTURE_2D,
		RS_TEXTURE_3D,
		RS_TEXTURING,
		RS_BLENDING,
		RS_BLEND_SRC_FUNC,
		RS_BLEND_DST_FUNC,
		RS_BLEND_OP,
		RS_BLEND_COLOR,
		RS_DEPTH_TEST,
		RS_DEPTH_FUNC,
		RS_ALPHA_TEST,
		RS_ALPHA_FUNC,
		RS_STENCIL_FUNC,
		RS_FILL_MODE,
		RS_SHADE_MODE,
		RS_POINT_SPRITE,
		RS_POINT_SIZE
	};

	struct TextureOp
	{
		enum 
		{
			REPLACE,			// Arg0
			MODULATE,			// Arg0 * Arg1
			ADD,					// Arg0 + Arg1
			SUBTRACT,			// Arg0 - Arg1
			INTERPOLATE		// Arg0 * (Arg2) + Arg1 * (1 - Arg2)
		};
	};

	struct TextureArg
	{
		enum
		{
			TEXTURE,	
			CONSTANT,
			DIFFUSE,	
			PREVIOUS
		};
	};

	//struct TextureUnitState
	//{
		enum  TextureUnitState
		{
			TS_COLOR_OP,
			TS_COLOR_ARG0,
			TS_COLOR_ARG1,
			TS_COLOR_ARG2,
			TS_ALPHA_OP,
			TS_ALPHA_ARG0,
			TS_ALPHA_ARG1,
			TS_ALPHA_ARG2,
			TS_FILTER_MODE,
			TS_ADDRESS_U,
			TS_ADDRESS_V,
			TS_ADDRESS_W,
			TS_TEXCOORD_INDEX,
			TS_POINT_SPRITE
		};
	//};

	struct TextureFilterMode
	{
		enum
		{
			POINT,
			LINEAR,
			BILINEAR,
			TRILINEAR,
			ANISOTROPIC
		};
	};

	struct TextureAdressMode
	{
		enum
		{
			WRAP,
			CLAMP,
			REPEAT,
			MIRROR
		};
	};

	struct Caps
	{
		int maxTextureUnits;
		int maxActiveLights;
	};

	/** Render state of the renderer. */
	class State
	{
	public:

		State()
		{
			_values[RS_CULLING] = false;
			_values[RS_CULL_MODE] = CullMode::BACK;
			_values[RS_LIGHTING] = false;
			_values[RS_TEXTURE_1D] = false;
			_values[RS_TEXTURE_2D] = false;
			_values[RS_TEXTURE_3D] = false;
			_values[RS_TEXTURING] = false;
			_values[RS_BLENDING] = false;
			_values[RS_BLEND_SRC_FUNC] = BlendMode::ONE;
			_values[RS_BLEND_DST_FUNC] = BlendMode::ZERO;
			_values[RS_BLEND_OP] = BlendOp::ADD;
			_values[RS_BLEND_COLOR] = false;
			_values[RS_DEPTH_TEST] = false;
			_values[RS_DEPTH_FUNC] = LESS;
			_values[RS_ALPHA_TEST] = false;
			_values[RS_ALPHA_FUNC] = ALWAYS;
			_values[RS_STENCIL_FUNC] = ALWAYS;
			_values[RS_FILL_MODE] = FillMode::SOLID;
			_values[RS_SHADE_MODE] = ShadeMode::GOURAUD;
			_values[RS_POINT_SPRITE] = false;
			_values[RS_POINT_SIZE] = 1;
		}

		const uint32& value(const RenderState& rs) const
		{
			return _values.find(rs)->second;
		}

		uint32& value(const RenderState& rs)
		{
			return _values.find(rs)->second;
		}

	private :
		std::map<RenderState, dw::uint32> _values;
	};

	struct RenderStates
	{
		
		bool normalizeNormals;

		bool lighting;
		
		bool culling;
		
		bool depthTest;
		
		bool alphaTest;
		
		CullMode cullMode;
		
		FillMode fillMode;
		
		ShadeMode shadeMode;
		
		CmpFunc depthFunc;
		
		CmpFunc alphaFunc;
		
		CmpFunc stencilFunc;
		
		BlendOp blendOp;
	};

	struct TextureUnitStates
	{

		TextureFilterMode minFilter;
		
		TextureFilterMode magFilter;
		
		TextureFilterMode mipFilter;
		
		TextureAdressMode addressU;
		
		TextureAdressMode addressV;
		
		TextureAdressMode addressW;
		
		uint32 elementIndex;

	};

	struct TextureUnit
	{
		class State
		{

		public:

			State() 
			{
				_values[TS_COLOR_OP]				= 0;
				_values[TS_COLOR_ARG0]			= 0;
				_values[TS_COLOR_ARG1]			= 0;
				_values[TS_COLOR_ARG2]			= 0;
				_values[TS_ALPHA_OP]				=	0;
				_values[TS_ALPHA_ARG0]			= 0;
				_values[TS_ALPHA_ARG1]			= 0;
				_values[TS_ALPHA_ARG2]			= 0;
				_values[TS_FILTER_MODE]			= TextureFilterMode::POINT;
				_values[TS_ADDRESS_U]				= TextureAdressMode::REPEAT;
				_values[TS_ADDRESS_V]				= TextureAdressMode::REPEAT;
				_values[TS_ADDRESS_W]				= TextureAdressMode::REPEAT;
				_values[TS_TEXCOORD_INDEX]	= 0;
			}

			const uint32& value(const TextureUnitState& rs) const { return _values.find(rs)->second; }

			uint32& value(const TextureUnitState& rs) { return _values.find(rs)->second; }

		private:

			typedef std::map<TextureUnitState, uint32> StateMap;

			typedef StateMap::iterator StateIterator;

			typedef StateMap::const_iterator StateConstIterator;

		private:

			StateMap _values;

		private:

			friend std::ostream& operator<< (std::ostream&, const TextureUnit::State&);
	
			friend std::istream& operator>> (std::istream&, TextureUnit::State&);

		};

		State state;
	};

public:

	Renderer();
	~Renderer();

	void setRenderTarget(RenderTarget* target);
	void beginRender();
	void endRender();

	const Caps& getCaps()		const { return _caps; }
	
	const std::string& getName()		const { return _name; }
	
	const std::string& getVendor()	const { return _vendor; }

	virtual void setRenderState(const RenderState& renderState, const uint32 value){};

	virtual void setViewport(const unsigned, const unsigned, const unsigned, const unsigned){};

	virtual void setMatrixMode	(const MatrixMode&){};
	
	virtual void setColor(const Colorf& color){};

	virtual void setColor(const float& r, const float& g, const float& b, const float& a){};
	
	virtual void setPerspective(double, double, double, double){};

	virtual void setOrtho2D(double, double, double, double, double, double){};

	virtual void pushState(){};

	virtual void popState(){};

	virtual void setState(const State&){};

	virtual void loadIdentity(){};
	
	virtual void pushMatrix(){};
	
	virtual void popMatrix(){};
	
	virtual void loadMatrix(const Matrix44f&){};
	
	virtual void multMatrix(const Matrix44f&){};

	virtual void setClipRect(const unsigned x, const unsigned y, const unsigned width, const unsigned height){};
	
	virtual ITexture* createTexture(){ return NULL; };

	VertexBuffer* createVertexBuffer(VertexBuffer::updates update, const VertexFormat* vertexFormat)
	{
		return new VertexBuffer(update, VertexBuffer::usage_draw, vertexFormat);
	};

	VertexBuffer* createVertexBuffer(VertexBuffer::updates update, VertexBuffer::usages usage, const VertexFormat* vertexFormat)
	{
		return new VertexBuffer(update, usage, vertexFormat);
	};

	void releaseVertexBuffer(VertexBuffer* buffer)
	{
		delete buffer;
	}

	IndexBuffer* createIndexBuffer(IndexBuffer::updates update, IndexBuffer::indextypes indexType)
	{
		return new IndexBuffer(update, IndexBuffer::usage_draw, indexType);
	}

	IndexBuffer* createIndexBuffer(IndexBuffer::updates update, IndexBuffer::usages usage, IndexBuffer::indextypes indexType)
	{
		return new IndexBuffer(update, usage, indexType);
	}

	void releaseIndexBuffer(IndexBuffer* buffer)
	{
		delete buffer;
	}

	virtual void setLight(const int i, Light* light){};

	virtual void setProgram(Program* program){};

	virtual void setTexture(ITexture* texture){};
	
	virtual void setTexture(const int textureUnit, ITexture* texture){};
	
	virtual void setTextureUnit(const int i){};

	virtual void setTextureUnitState(const int i, const TextureUnitState& textureUnitState, const uint32 value){};
	
	/** Set the vertex stream source. */
	virtual void setVertexBuffer(VertexBuffer* vertexBuffer){};
	
	virtual void setIndexBuffer(IndexBuffer* indexBuffer){};

	void drawIndexedPrimitive(primitive::types primitiveType, dw::uint32 indexStart, dw::uint32 indexCount);
	void drawIndexedPrimitive(primitive::types primitiveType, dw::uint32 indexStart, dw::uint32 indexCount,
		dw::uint32 indexMin, dw::uint32 indexMax);

	virtual void drawRect(const unsigned x, const unsigned y, const unsigned width, const unsigned height, const bool filled) const{};

	virtual void drawCircle(const unsigned radius){};

	virtual void drawBox(const float& width, const float& height, const float& depth){};

	virtual void drawBox(const Cuboid&){};

	virtual void drawSphere(const float& radius){};

	virtual void drawTeapot(const float& size){};

	virtual void drawText(Font* font, const std::string& text){};

	virtual void drawGrid(const int size, const int unit){};

protected:

	typedef std::stack< State >							StateStack;

	typedef std::stack< Matrix4f >					MatrixStack;

protected:

	/** Renderer name. */
	std::string _name;

	std::string _vendor;
	
	std::string _version;
	
	/**	Capabilities of the renderer. */
	Caps _caps;

	State* _currentState;

	StateStack _stateStack;

	Matrix44f* _matrix;
	
	MatrixStack _matrixStack;

	TextureUnit* _textureUnits;
	
	uint32 _currentTextureUnit;

	Material* _currentMaterial;
	
	Font* _currentFont;

	ITexture* _currentTexture;
	
	IndexBuffer* m_indexBuffer;
	
	Program* _currentProgram;
};

	}
}

#endif // !DW_GRAPHICS_RENDERER_H
